package com.yeban.pattem.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * @Desc TODO
 * @Author yeban
 * @Date 2025/11/6
 *
 * Sping的AOP、事务默认使用的是JDK动态代理，若没有使用接口就会使用CGLIB
 *
 * 一、JDK动态代理
 * 概念：拦截对真实对象方法的直接访问，增强真实对象方法的功能
 *
 * 二、JDK动态代理
 * 1. 代理对象，不需要实现接口，但是目标对象要实现接口，否则不能用动态代理
 * 2. 代理对象的生成，是利用JDK的API，动态的在内存中构造代理对象
 * 3. 动态代理也叫做：JDK代理，接口代理
 *
 * 三、JDK动态代理优缺点
 * 优点：
 * 1. 实现不变动目标方法的基础上，对原有方法上实现了增强
 * 2. 实现解耦
 * 缺点：
 * 1. 目标对象仍然实现接口
 */
public class DynamicProxyBankLogService {

    // 目标对象
    private IService target;

    public DynamicProxyBankLogService(IService target) {
        this.target = target;
    }

    public IService getProxy() {
        // 代理对象
        IService proxy = null;
        // 1.类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        // 2.实现的接口
        Class<?>[] interfaces = target.getClass().getInterfaces();
        // 3.执行处理器
        InvocationHandler handler = new InvocationHandler() {
            /**
             * @param proxy     代理对象
             * @param method    目标方法对象
             * @param args      目标方法的形参列表
             * @return          目标方法的返回值，若目标方法没有返回值则为null
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 获得执行的目标方法的方法名称
                String name = method.getName();
                System.out.println("开始日志追踪：The Method " + name + " begin with" + Arrays.toString(args));
                long l1 =System.currentTimeMillis();
                // 执行目标方法的前后，执行日志追踪
                Object result = method.invoke(target, args);
                long l2 =System.currentTimeMillis();
                System.out.println("日志追踪结束：The Method " + name + " with , 耗时" + (l2-l1) + "ms");
                return result;
            }
        };

        proxy = (IService) Proxy.newProxyInstance(classLoader, interfaces, handler);
        return proxy;
    }
}
